package org.itt.cp.pec;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.log4j.Logger;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptionCharEscapeMap;
import org.apache.xmlbeans.XmlOptions;
import org.itt.FooUtil;
import org.itt.Properties;
import org.itt.ap.PECMap;
import org.itt.ap.Source;
import org.itt.ap.Writer;
import org.itt.schemas.indexcsost.ROOTDocument;
import org.itt.schemas.indexcsost.ROOTDocument.ROOT.DOCUMENTS;
import org.itt.schemas.indexcsost.ROOTDocument.ROOT.DOCUMENTS.DOCUMENT;
import org.itt.schemas.indexcsost.ROOTDocument.ROOT.DOCUMENTS.DOCUMENT.DOCUMENTPAGES;
import org.itt.schemas.indexcsost.ROOTDocument.ROOT.DOCUMENTS.DOCUMENT.DOCUMENTPAGES.PAGE;
import org.itt.schemas.indexcsost.ROOTDocument.ROOT.DOCUMENTS.DOCUMENT.INDEXFIELDS;
import org.itt.schemas.indexcsost.ROOTDocument.ROOT.DOCUMENTS.DOCUMENT.INDEXFIELDS.FIELD;
import org.itt.schemas.indexcsost.ROOTDocument.ROOT.DOCUMENTS.MULTIINDICE;
import org.itt.schemas.indexcsost.ROOTDocument.ROOT.INFO;
import org.itt.schemas.indexcsost.ROOTDocument.ROOT.INFO.HASHTYPE;

public class PECWriter implements Writer {
        int numOfIndexFiles = 0;
        int numOfXmlFiles = 0;
        Path oldDirPath = null;

        private static Logger logger = Logger.getLogger("PECWriter");

        @Override
        public void index(Source source) {
                try {
                        logger.info("Inizio il processo di scrittura");
                       
                        String indexFileName = null;
                        int mapSize = 0;
                        int extIteration = 1;
                        int intIteration;

                        int year = Calendar.getInstance().get(Calendar.YEAR);
                        int month = Calendar.getInstance().get(Calendar.MONTH) + 1;
                        int day = Calendar.getInstance().get(Calendar.DAY_OF_MONTH);

                        int hours = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
                        int minutes = Calendar.getInstance().get(Calendar.MINUTE);
                        int seconds = Calendar.getInstance().get(Calendar.SECOND);

                        logger.debug("Memorizzo l'istante d'inizio elaborazione");
                        long start = System.nanoTime();

                        String dateSuffix = "" + year + (month > 9 ? month : ("0" + month))
                                        + (day > 9 ? day : ("0" + day))
                                        + (hours > 9 ? hours : ("0" + hours))
                                        + (minutes > 9 ? minutes : ("0" + minutes))
                                        + (seconds > 9 ? seconds : ("0" + seconds));
                       
                        logger.debug("Memorizzo il suffisso dei file ZIP: " + dateSuffix);

                        logger.debug("Estraggo il Customer ID dal file di properties");
                        Integer customerId = Integer.valueOf(Properties.get().INDEX_CUSTOMER_ID);
                        logger.debug("INDEX_CUSTOMER_ID: " + customerId);
                       
                        logger.debug("Estraggo il Customer Name dal file di properties");
                        String customerName = Properties.get().CUSTOMER_NAME;
                        logger.debug("CUSTOMER_NAME: " + customerName);

                        logger.debug("Estraggo il Flow ID dal file di properties");
                        Integer flowId = Integer.valueOf(Properties.get().FLOW_ID);
                        logger.debug("FLOW_ID: " + flowId);
                       
                        logger.debug("Estraggo il valore Chunk dal file di properties");
                        Integer chunk = Integer.valueOf(Properties.get().CHUNK);
                        logger.debug("CHUNK: " + chunk);

                        if (chunk < 1) {
                                logger.debug("Il valore del Chunk  minore di 1: non viene considerato");
                                chunk = mapSize;
                        }

                        logger.debug("Estraggo dall'oggetto source la hashmap contenente gli oggetti PEC");
                        PECMap map = source.getPECMap();
                       
                        logger.debug("Calcolo la dimensione della hashmap");
                        mapSize = map.size();

                        logger.debug("Dimensione della hashmap: " + mapSize);
                       
                        logger.debug("Mi preparo ad iterare sulla hashmap");
                        Set<Entry<String, List<Source>>> entrySet = map.entrySet();
                       
                        Iterator<Entry<String, List<Source>>> iterator = entrySet
                                        .iterator();
                       
                        while (mapSize > 0) {
                                ROOTDocument doc = null;
                                File indexFile = null;

                                intIteration = 1;
                               
                                String newPathFile = null;
                                String oldPathFile = null;

                                String[] emlArray = new String[chunk];

                                logger.debug("Preparo il file ZIP");
                                indexFileName = "PEC" + "_" + flowId + "_" + customerName + "_" + dateSuffix + "_" + extIteration;
                                logger.debug("Il nome del file ZIP : " + indexFileName);
                               
                                logger.debug("Preparo il file di indice XML");
                                indexFile = new File(Properties.get().WORKDIR, indexFileName + ".xml");
                                logger.debug("Il nome del file di indice XML : " + indexFile);
                               
                                if (!indexFile.exists()) {
                                        indexFile.getParentFile().mkdirs();
                                }

                                logger.info("Inizio a costruire il contenuto del file di indice XML");
                                doc = ROOTDocument.Factory.newInstance();
                                logger.debug("<ROOT>");
                                ROOTDocument.ROOT root = doc.addNewROOT();

                                logger.debug("\t<INFO>");
                                INFO info = root.addNewINFO();
                               
                                logger.debug("\t\t<LOADDOCUMENTPATH>");
                                info.setLOADDOCUMENTPATH(indexFileName);
                               
                                logger.debug("\t\t<CUSTOMERID>");
                                info.setCUSTOMERID(customerId);
                               
                                logger.debug("\t\t<INDEX_PIVA_COD_FISC>");
                                info.setPIVACODFISC(Properties.get().INDEX_PIVA_COD_FISC);
                               
                                logger.debug("\t\t<INDEX_MAP_ID>");
                                info.setMAPID(Byte.valueOf(Properties.get().INDEX_MAP_ID));
                               
                                logger.debug("\t\t<COURIERNAME>");
                                info.setCOURIERNAME(indexFileName);
                               
                                logger.debug("\t\t<INDEX_SIGN_SINGLEDOC>");
                                info.setSIGNSINGLEDOC(Properties.get().INDEX_SIGN_SINGLEDOC);

                                logger.debug("\t\t<HASHTYPE>");
                                HASHTYPE hashType = info.addNewHASHTYPE();
                               
                                hashType.setCODE(Properties.get().INDEX_HASH_TYPE_CODE);
                                hashType.setStringValue(Properties.get().INDEX_HASH_TYPE);

                                logger.debug("\t<DOCUMENTS>");
                                DOCUMENTS documents = root.addNewDOCUMENTS();
                               
                                logger.debug("\t\t<FAMILY>");
                                documents.setFAMILY(Integer.valueOf(Properties.get().INDEX_FAMILY_ID));

                                logger.debug("\t\t<MULTIINDICE>");
                                MULTIINDICE multiIndice = documents.addNewMULTIINDICE();
                                multiIndice.setIDPADRE("PATH_FILE");
                                multiIndice.setIDFIGLIO("PATH_FILE_FATHER");

                                while (intIteration <= chunk && mapSize > 0) {
                                        String hash = null;
                                       
                                        Entry<String, List<Source>> m = iterator.next();
                                        List<Source> pl = (List<Source>) m.getValue();
                                       
                                        while (!pl.isEmpty()) {
                                                PEC pec = (PEC) pl.get(0);

                                                int length = pec.getDestinatari().size();

                                                for (int i = 0; i < length; i++) {
                                                        String d = pec.getDestinatari().get(i);
                                                        String td = pec.getTipiDestinatari().get(i);
                                                        String risp = pec.getRisposte().get(i);
                                                        String ric = pec.getRicezioni().get(i);
                                                        String cons = pec.getConsegne().get(i);

                                                        logger.debug("\t\t<DOCUMENT>");
                                                        DOCUMENT document = documents.addNewDOCUMENT();
                                                       
                                                        logger.debug("\t\t\t<OPTYPE>");
                                                        document.setOPTYPE("INPUT");
                                                       
                                                        logger.debug("\t\t\t<DOCDATE>");
                                                        document.setDOCDATE(pec.getData());
                                                       
                                                        logger.debug("\t\t\t<INDEXFIELDS>");
                                                        INDEXFIELDS indexFields = document.addNewINDEXFIELDS();

                                                        if (i > 0) {
                                                                logger.debug("\t\t\t\t<PATH_FILE_FATHER>");
                                                                FIELD PATH_FILE_FATHER = indexFields.addNewFIELD();
                                                                PATH_FILE_FATHER.setNAME("PATH_FILE_FATHER");
                                                                PATH_FILE_FATHER.setVALUE(pec.getPathFileFather());
                                                        }

                                                        logger.debug("\t\t\t\t<PATH_FILE>");
                                                        FIELD PATH_FILE = indexFields.addNewFIELD();
                                                        PATH_FILE.setNAME("PATH_FILE");
                                                        PATH_FILE.setVALUE(pec.getPathFile());
                                                       
                                                        oldPathFile = Properties.get().WORKDIR + File.separator + pec.getDirectory() + File.separator + pec.getPathFile();
                                                        newPathFile = Properties.get().WORKDIR + File.separator + indexFileName + File.separator + PATH_FILE.getVALUE();
                                                       
                                                        logger.debug("\t\t\t\t<NOME_EMAIL>");
                                                        FIELD NOME_EMAIL = indexFields.addNewFIELD();
                                                        NOME_EMAIL.setNAME("NOME_EMAIL");
                                                        NOME_EMAIL.setVALUE(pec.getPathFile());

                                                        logger.debug("\t\t\t\t<CLIENTE>");
                                                        FIELD CLIENTE = indexFields.addNewFIELD();
                                                        CLIENTE.setNAME("CLIENTE");
                                                        CLIENTE.setVALUE(pec.getCliente());

                                                        logger.debug("\t\t\t\t<MAILBOX>");
                                                        FIELD MAILBOX = indexFields.addNewFIELD();
                                                        MAILBOX.setNAME("MAILBOX");
                                                        MAILBOX.setVALUE(String.valueOf(pec.getMailbox()));

                                                        logger.debug("\t\t\t\t<MESE>");
                                                        FIELD MESE = indexFields.addNewFIELD();
                                                        MESE.setNAME("MESE");
                                                        MESE.setVALUE(pec.getMese());

                                                        logger.debug("\t\t\t\t<ANNO>");
                                                        FIELD ANNO = indexFields.addNewFIELD();
                                                        ANNO.setNAME("ANNO");
                                                        ANNO.setVALUE(pec.getAnno());

                                                        logger.debug("\t\t\t\t<TIPO_MESSAGGIO>");
                                                        FIELD TIPO_MESSAGGIO = indexFields.addNewFIELD();
                                                        TIPO_MESSAGGIO.setNAME("TIPO_MESSAGGIO");
                                                        TIPO_MESSAGGIO.setVALUE(pec.getTipoMessaggio());

                                                        logger.debug("\t\t\t\t<MITTENTE>");
                                                        FIELD MITTENTE = indexFields.addNewFIELD();
                                                        MITTENTE.setNAME("MITTENTE");
                                                        MITTENTE.setVALUE(String.valueOf(pec.getMittente()));

                                                        logger.debug("\t\t\t\t<DESTINATARIO>");
                                                        FIELD DESTINATARIO = indexFields.addNewFIELD();
                                                        DESTINATARIO.setNAME("DESTINATARIO");
                                                        DESTINATARIO.setVALUE(d);

                                                        logger.debug("\t\t\t\t<TIPO_DESTINATARIO>");
                                                        FIELD TIPO_DESTINATARIO = indexFields.addNewFIELD();
                                                        TIPO_DESTINATARIO.setNAME("TIPO_DESTINATARIO");
                                                        TIPO_DESTINATARIO.setVALUE(td);

                                                        logger.debug("\t\t\t\t<OGGETTO>");
                                                        FIELD OGGETTO = indexFields.addNewFIELD();
                                                        OGGETTO.setNAME("OGGETTO");
                                                        OGGETTO.setVALUE(pec.getOggetto());

                                                        logger.debug("\t\t\t\t<TIMEZONE>");
                                                        FIELD TIMEZONE = indexFields.addNewFIELD();
                                                        TIMEZONE.setNAME("TIMEZONE");
                                                        TIMEZONE.setVALUE(pec.getTimezone());

                                                        logger.debug("\t\t\t\t<DATA>");
                                                        FIELD DATA = indexFields.addNewFIELD();
                                                        DATA.setNAME("DATA");
                                                        DATA.setVALUE(pec.getData());

                                                        logger.debug("\t\t\t\t<ORA>");
                                                        FIELD ORA = indexFields.addNewFIELD();
                                                        ORA.setNAME("ORA");
                                                        ORA.setVALUE(pec.getOra());

                                                        logger.debug("\t\t\t\t<IDENTIFICATIVO_MSGID>");
                                                        FIELD IDENTIFICATIVO_MSGID = indexFields.addNewFIELD();
                                                        IDENTIFICATIVO_MSGID.setNAME("IDENTIFICATIVO_MSGID");
                                                        IDENTIFICATIVO_MSGID.setVALUE(String.valueOf(pec.getIdentificativoMsgId()));

                                                        logger.debug("\t\t\t\t<RISPOSTE>");
                                                        FIELD RISPOSTE = indexFields.addNewFIELD();
                                                        RISPOSTE.setNAME("RISPOSTE");
                                                        RISPOSTE.setVALUE(risp);

                                                        logger.debug("\t\t\t\t<RIFMSGID>");
                                                        FIELD RIFMSGID = indexFields.addNewFIELD();
                                                        RIFMSGID.setNAME("RIFMSGID");
                                                        RIFMSGID.setVALUE(pec.getRifMsgId());

                                                        logger.debug("\t\t\t\t<TIPO_RICEVUTA>");
                                                        FIELD TIPO_RICEVUTA = indexFields.addNewFIELD();
                                                        TIPO_RICEVUTA.setNAME("TIPO_RICEVUTA");
                                                        TIPO_RICEVUTA.setVALUE(pec.getTipoRicevuta());

                                                        logger.debug("\t\t\t\t<CONSEGNA>");
                                                        FIELD CONSEGNA = indexFields.addNewFIELD();
                                                        CONSEGNA.setNAME("CONSEGNA");
                                                        CONSEGNA.setVALUE(cons);

                                                        logger.debug("\t\t\t\t<RICEZIONE>");
                                                        FIELD RICEZIONE = indexFields.addNewFIELD();
                                                        RICEZIONE.setNAME("RICEZIONE");
                                                        RICEZIONE.setVALUE(ric);

                                                        logger.debug("\t\t\t<DOCUMENTPAGES>");
                                                        DOCUMENTPAGES documentPages = document.addNewDOCUMENTPAGES();

                                                        logger.debug("\t\t\t\t<PAGE>");
                                                        PAGE page = documentPages.addNewPAGE();

                                                       
                                                        if (hash == null) {
                                                                hash = FooUtil.calculateHash(files(pec,indexFileName));
                                                        }

                                                        page.setHASH(hash);
                                                        page.setStringValue(pec.getPathFile());

                                                }
                                               
                                                logger.debug("oldPathFile: " + oldPathFile);
                                                logger.debug("newPathFile: " + newPathFile);
                                               
                                                // Creo una parent directory che finir? nello zip
                                                String fileName = new File(oldPathFile).getName();
                                                logger.debug("fileName: " + fileName);
                                                String fromDir = new File(oldPathFile).getParent();
                                                logger.debug("fromDir: " + fromDir);
                                                String toDir = new File(newPathFile).getParent();
                                                logger.debug("toDir: " + toDir);
                                               
                                                if(new File(toDir).mkdir()) {
                                                        logger.debug("Directory " + toDir + " creata con successo");
                                                } else {
                                                        logger.error("Si  verificato un problema nella creazione della directory " + toDir);
                                                }
                                               
                                                Files.move(
                                                                Paths.get(fromDir + File.separator + fileName),
                                                                Paths.get(toDir + File.separator + fileName),
                                                                StandardCopyOption.REPLACE_EXISTING);

                                                logger.debug("File spostato da" + fromDir + File.separator + fileName + " a " + toDir + File.separator + fileName);
                                               
                                                emlArray[intIteration - 1] = newPathFile;
                                                mapSize--;
                                                intIteration++;

                                                pl = pl.subList(1, pl.size());
                                        }
                                }

                                extIteration++;

                                logger.debug("Inizio la costruzione del file di indice XML");
                                XmlOptions opts = new XmlOptions();
                                opts.setCharacterEncoding("iso-8859-1");
                                opts.setUseDefaultNamespace();

                                opts.setSavePrettyPrint();
                                opts.setCompileNoAnnotations();

                                XmlOptionCharEscapeMap escapes = new XmlOptionCharEscapeMap();
                                escapes.addMapping('>', XmlOptionCharEscapeMap.PREDEF_ENTITY);
                                opts.setSaveSubstituteCharacters(escapes);

                                logger.debug("Salvo il file di indice XML: " + indexFile);
                                doc.save(indexFile, opts);

                                logger.debug("Inizio il processo di creazione del file ZIP");
                                createZip(indexFile, emlArray);

                                Path oldFilePath = indexFile.toPath();
                                Files.delete(oldFilePath);

                                oldDirPath = oldFilePath.getParent();

                                Path tempFilePath = null;

                                for (int i = 0; i < emlArray.length; i++) {
                                        if (emlArray[i] == null) {
                                                break;
                                        }

                                        tempFilePath = Paths.get(emlArray[i]);
                                        Files.delete(tempFilePath);
                                }

                                Path tempDirPath = tempFilePath.getParent();
                                Files.delete(tempDirPath);

                                numOfIndexFiles++;

                                System.gc();

                                // Terminata la scrittura di tutti i file indice xml
                                if (mapSize == 0) {
                                        logger.info("Ho creato "
                                                        + (extIteration - 1)
                                                        + " file zip");
                                       
                                        logger.info("Durata creazione file zip: " + FooUtil.prettyPrintTime(System.nanoTime() - start));

                                        logger.info("Elaborazione terminata con successo");
                                        System.exit(0);
                                }
                        }
                } catch (NumberFormatException | XmlException | IOException e) {
                        logger.error(e);
                        System.exit(1);
                }
        }

        @Override
        public byte[] files(Source source, String fileName) {
                byte[] fileByteArray = null;

                try {
                        PEC pec = (PEC) source;
                        fileByteArray = Files.readAllBytes(Paths.get(Properties.get().WORKDIR + File.separator + pec.getDirectory()
                                        + File.separator + pec.getPathFile()));
                } catch (NullPointerException | IOException e) {
                        logger.error(e);
                        System.exit(1);
                }

                return fileByteArray;
        }

        @Override
        public void createZip(File indexFile, String[] emlArray) {
                // Questi sono i file da includere nello zip
                String[] filesToZip = new String[emlArray.length - 1];

                filesToZip[0] = Properties.get().WORKDIR + File.separator + indexFile.getName();
               
                logger.info("Primo file da aggiungere allo zip: " + filesToZip[0]);
               

                for (int i = 1; i < filesToZip.length; i++) {
                        filesToZip[i] = emlArray[i - 1];
                        logger.info("Altro file da aggiungere allo zip: " + filesToZip[i]);
                }

                // Creo un buffer per leggere i file
                byte[] buf = new byte[1024];

                try {
                        // Creo il file zip
                        String target = Properties.get().WORKDIR + File.separator + indexFile.getName().substring(0,
                                        indexFile.getName().lastIndexOf('.'))
                                        + ".zip";

                        // System.out.println("Creating zip: " + target);
                        logger.info("Sto creando il file zip: " + target);

                        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(
                                        target));

                        // Comprimo i file
                        for (int i = 0; i < filesToZip.length; i++) {
                                if (filesToZip[i] == null) {
                                        break;
                                }

                                FileInputStream in = new FileInputStream(filesToZip[i]);

                                logger.info("Aggiungo il file " + filesToZip[i] + " allo zip");
                                out.putNextEntry(new ZipEntry(filesToZip[i]));

                                // Trasferisco i byte dal file allo zip
                                int len;

                                while ((len = in.read(buf)) > 0) {
                                        out.write(buf, 0, len);
                                }

                                // Chiudo la ZIP entry
                                out.closeEntry();
                                in.close();
                        }

                        // Chiudo il file zip
                        out.close();
                } catch (IOException e) {
                        logger.error(e);
                        System.exit(1);
                }
        }

}
